#include  "PacketCombiner.h"

#include "RstpComm.h"
#include "MyAssert.h"
#include "LogInterface.h"

CPacketCombiner::CPacketCombiner()
{
	m_RecvDataLen = 0;
	m_ChanID = 0;
	m_RequestID = 0;
}

CPacketCombiner::~CPacketCombiner()
{
	while(!m_mapData.empty())
	{
		delete m_mapData.begin()->second;
		m_mapData.erase(m_mapData.begin());
	}
}


void CPacketCombiner::Initialize(UInt8 inChanID, UInt16 inRequest)
{
	m_ChanID = inChanID;
	m_RequestID = inRequest;

	Assert(m_mapData.empty());
	m_mapData.clear();
	m_RecvDataLen = 0;
}

/*
 * copy data into internal buffer and check if all data are recved
 * INPUT:
 * OUTPUT:
 * RETURN:
 *		INPUT_FINISH: all data arrived
 *		INPUT_OK: current data has been saved
 *		INPUT_ERR: fetal error
 *		INPUT_DUP: data duplicate
 */
CPacketCombiner::INPUT_RET CPacketCombiner::InputData(char *pBuff, UInt16 inLen)
{
	Assert(pBuff);
	if(!pBuff)
		return INPUT_ERR;

	//handle data
	SRstpCommHead sCommHead;
	if(sCommHead.Parse(pBuff,inLen))
	{
		//check if the packet is expected
		if(sCommHead.ChnID == m_ChanID && sCommHead.RequestID == m_RequestID)
		{
			//check if the data is out of bound
			if(sCommHead.SeqNo <= sCommHead.Total)
			{
				//check if the data is duplicat
				DataMapIterator it = m_mapData.find(sCommHead.SeqNo);
				if(it == m_mapData.end())
				{//not found, so add it
					SData *data = new SData;
					if(!data)
					{
						ERR_LOGGER2("Allocate memory failed");
						return INPUT_ERR;
					}

					data->Len = inLen - sCommHead.Size();
					data->pData = new char[data->Len];
					if(data->pData)
					{
						memcpy(data->pData, pBuff + sCommHead.Size(), data->Len);

						m_mapData[sCommHead.SeqNo] = data;
						m_RecvDataLen += data->Len;

						//check if all data are received
						if(m_mapData.size() == sCommHead.Total)
							return INPUT_FINISH;

						return INPUT_OK;
					}
					else
					{
						delete data;
						ERR_LOGGER2("Allocate memory failed");
						return INPUT_ERR;
					}
				}
				else 
				{
					ERR_LOGGER2( "Recv duplicate data");
					return INPUT_DUP;
				}
			}
		}
	}

	return INPUT_ERR;
}

/*
 * get current received data length
 */
UInt32  CPacketCombiner::GetRecvDataLen()
{
	return m_RecvDataLen;
}

/*
 * dump data into buffer
 * INPUT:
 *	inBuffLen: make sure it is above actual received data length
 * OUTPUT:
 *	outLen: act data len
 * RETURN:
 *	OS_NoErr: dump ok
 *	OS_Err: dump failed, maybe out of bound
 */
Int32   CPacketCombiner::DumpData(char *pBuff, UInt32 inBuffLen, UInt32 *outLen)
{
	char *pOutBuff = pBuff;
	UInt32 iAcc = 0;
	for(DataMapIterator it = m_mapData.begin(); it != m_mapData.end(); ++it)
	{
		//check if out of buffer bound
		SData *pData = it->second;
		if(iAcc + pData->Len <= inBuffLen)
		{
			memcpy(pOutBuff, pData->pData, pData->Len);

			//for next loop
			iAcc +=pData->Len;
			pOutBuff +=pData->Len;
		}
		else
		{
			ERR_LOGGER2("out of buffer bound");
			return OS_Err;
		}
	}

	*outLen = iAcc;
	return OS_NoErr;
}

